﻿using AutoMapper;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Mvc;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using Sino.CapacityCloud.Infrastructure.Features;
using Sino.CapacityCloud.Repositories.Models;
using Sino.CapacityCloud.Services;
using Sino.CapacityCloud.WebApi.ViewModels;
using Sino.Tms.Primitives;
using Sino.Tms.WebApi.Framework.Primitives;
using Sino.WebApi.Framework.Responses;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Z.EntityFramework.Plus;

namespace Sino.CapacityCloud.WebApi.ApiControllers
{

    public partial class UserController
        : ApiController<UserController>
    {

        private readonly ITmsServiceBus _tmsService;
        private readonly IChattingService _chattingService;
        private readonly IRandomPasswordService _randomPasswordService;

        public UserController(
            IHostingEnvironment env,
            IApplicationEnvironment applicationEnvironment,
            IDistributedCache cache,
            IChattingService chattingService,
            ITokenGenertationService tokenGenertationService,
            IRandomPasswordService randomPasswordService,
            ApplicationDbContext dbContext,
            ApplicationUserManager userManager,
            ApplicationRoleManager roleManager,
            ApplicationSignInManager signInManager,
            ILoggerFactory loggerFactory,
            ITmsServiceBus tmsService)
            : base(env, applicationEnvironment, cache, tokenGenertationService, dbContext, userManager, roleManager, signInManager, loggerFactory)
        {
            this._chattingService = chattingService;
            this._tmsService = tmsService;
            this._randomPasswordService = randomPasswordService;
        }

        public async Task<JsonResult> GetMyRating()
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    if (user.Car == null)
                    {
                        user.Car = this.DataContext.Cars.FirstOrDefault(x => x.CarId == user.CarId);
                    }
                    if (user.Car == null)
                    {
                        return await this.PackageResultAsync<MyRatingInfoResponse>();
                    }
                    var ratingsResponse = await this._tmsService.QueryEvaluationsAsync(null, user.Id);
                    var ratings = ratingsResponse?.Output?.List;
                    if (ratings != null && ratings.Count() > 0)
                    {
                        var response = new MyRatingInfoResponse()
                        {
                            // TODO: Fix it! Bind the rating to the user.
                            Ratings = ratings.Select(x => new OrderRating()
                            {
                                Date = SystemUtils.Try(() => DateTime.Parse(x.CreationTime)),
                                Attitude = x.Attitude,
                                Integrity = x.Credibility,
                                Timeliness = x.Timeliness,
                                TransPrice = x.Freight,
                                Comment = x.Content,
                                Name = null,
                                TotalPoint = (x.Attitude + x.Credibility + x.Timeliness + x.Freight) / 4.0f
                            }).ToArray()
                        };
                        return await this.PackageResultAsync<MyRatingInfoResponse>(response);
                    }
                    return await this.PackageResultAsync<MyRatingInfoResponse>();
                }
                throw new SystemException("Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get your rating info because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<MyRatingInfoResponse>(ex);
            }
        }

        public async Task<JsonResult> GetRealNameInfo()
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    if (user.Car == null) user.Car = this.DataContext.Cars.AsNoTracking().FirstOrDefault(x => x.CarId == user.CarId);
                    var response = new RealNameInfoResponse()
                    {
                        Info = user.GetRealNameInfo()
                    };
                    return await this.PackageResultAsync<RealNameInfoResponse>(response);
                }
                throw new SystemException("Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get your real name info because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<RealNameInfoResponse>(ex);
            }
        }

        public async Task<JsonResult> GetUserInfo()
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    if (user.Car == null) user.Car = this.DataContext.Cars.FirstOrDefault(x => x.CarId == user.CarId);
                    //if (user.Car == null)
                    //{
                    //    throw new SystemException($"Cannot find a car for the user {user.Id}.");
                    //}
                    var carLength = user.Car?.Length.GetCustomAttribute<CarLength, EnumTextMetaAttribute>(e => e == CarLength.None ? "" : null).Text;
                    try
                    {
                        var apiFeature = SystemUtils.Try(() => this.HttpContext.Features[typeof(IApiCompatibilityFeature)] as IApiCompatibilityFeature);
                        if (apiFeature != null)
                        {
                            if (new Version(apiFeature.ApiVersion) == new Version("1.0.0"))
                            {
                                carLength = carLength.Replace("6米", "6.0米");
                            }
                        }
                    }
                    catch
                    {
                        carLength = "";
                    }
                    var response = new UserInfoResponse()
                    {
                        Info = new UserInfo()
                        {
                            UserId = user.Id,
                            UserName = user.PhoneNumber,
                            PhoneNumber = user.PhoneNumber,
                            RealNameState = (RealNameState)user.IdentityCardCertificationState,
                            RealName = user.RealName,
                            HeadImage = user.AvatarKey,
                            Address = user.Address,
                            Point = user.Points.FirstOrDefault(x => x.UserId == user.Id)?.TotalPoints ?? 0.0f,
                            Profile = user.Profile,
                            CarNumber = user.Car?.Code,
                            CarLength = carLength,
                            CarType = user.Car?.Type.GetCustomAttribute<VehicleType, EnumTextMetaAttribute>(e => e == VehicleType.None ? "" : null).Text,
                            AuthorizedLoad = user.Car?.AssignmentOfLoad ?? 0.0,
                            CarriageType = (CarriageType)user.CarriageType
                        }
                    };
                    return await this.PackageResultAsync<UserInfoResponse>(response);
                }
                throw new SystemException("Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to get your user info because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<UserInfoResponse>(ex);
            }
        }

        [HttpPost]
        public async Task<JsonResult> ModifyUserInfo([FromBody]UserInfo info, string propertyName)
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    await this.UpdateUserInfoAsync(user, info, propertyName, false);
                    return await this.PackageResultAsync<VoidResponse>();
                }
                throw new SystemException("Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to save user info because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<VoidResponse>(ex);
            }
        }

        [HttpPost]
        public async Task<JsonResult> PutUserInfo([FromBody]UserInfo info)
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    await UpdateUserInfoAsync(user, info);
                    return await this.PackageResultAsync<VoidResponse>();
                }
                throw new SystemException("Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to save user info because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<VoidResponse>(ex);
            }
        }

        [HttpPost]
        public async Task<JsonResult> PutRealNameInfo(RealNameType type, [FromForm]string imageUri)
        {
            try
            {
                var user = await this.UserManager.FindByNameAsync(this.User.Identity.Name);
                if (user != null)
                {
                    var errorList = new List<string>();
                    try
                    {
                        this.UpdateUserRealNameInfo(user, type, imageUri, false);
                    }
                    catch (Exception ex)
                    {
                        errorList.Add(ex.Message + Environment.NewLine + ex.StackTrace);
                    }
                    var identityResult = await this.UserManager.UpdateAsync(user);
                    if (!identityResult.Succeeded)
                    {
                        var identityErrors = identityResult.Errors.Select(x => $"Code: {x.Code}, Description: {x.Description}").ToList();
                        errorList.AddRange(identityErrors);
                    }
                    var response = new PutRealNameInfoResponse()
                    {
                        IsSuccessed = errorList.Count == 0 && identityResult.Succeeded,
                        Message = string.Join(Environment.NewLine, errorList)
                    };
                    return await this.PackageResultAsync<PutRealNameInfoResponse>(response);
                }
                throw new SystemException("Failed to find the specified ApplicationUser object.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to save real name info because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<PutRealNameInfoResponse>(ex);
            }
        }

    }

}